<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0"
    />
    <title>策略模式-实现缓动动画</title>
</head>
<style lang="css">
    div {
        border-radius: 50%;
        width: 46px;
        height: 46px;
        line-height: 46px;
        text-align: center;
        color: rgb(0, 0, 0);
    }
</style>
<body>
    <p>这些算法都接受 4个参数，这 4个参数的含义分别是动画已消耗的时间、小球原始位置、小球目标位置、动画持续的总时间，返回的值则是动画元素应该处在的当前位置</p>
    <strong>使用策略模式实现缓动动画</strong>

    <div style="position:absolute;background:blue" id="div1">div1</div>
    <div style="position:absolute;background:red" id="div2">div2</div>
    <div style="position:absolute;background:pink" id="div3">div3</div>
    <script type="text/javascript">
        var tween = {
            linear: function (t, b, c, d) {
                return c * t / d + b
            },
            easeIn: function (t, b, c, d) {
                return c * (t /= d) * t + b
            },
            strongEaseIn: function (t, b, c, d) {
                return c * (t /= d) * t * t * t * t + b
            },
            strongEaseOut: function (t, b, c, d) {
                return c * ((t = t / d - 1) * t * t * t * t + 1) + b
            },
            sineaseIn: function (t, b, c, d) {
                return c * (t /= d) * t * t + b
            },
            sineaseOut: function (t, b, c, d) {
                return c * ((t = t / d - 1) * t * t + 1) + b
            }
        }
        var Animate = function (dom) {
            this.dom = dom
            this.startTime = 0 // 开始时间
            this.startPos = 0 // 开始位置
            this.endPos = 0 // 结束位置
            this.properName = null
            this.easing = null // 缓动算法
            this.duration = null // 持续时间
        }
        Animate.prototype.start = function (properName, endPos, duration, easing) {

            this.startTime = +new Date // 动画启动时间,+转换为时间戳
            this.startPos = this.dom.getBoundingClientRect()[properName] // dom 节点初始位置
            this.endPos = endPos
            this.properName = properName
            this.duration = duration
            this.easing = tween[easing]

            var self = this

            var timeId = setInterval(function () { // 启动定时器，开始执行动画
                if (self.step() === false) { // 如果动画已结束，则清除定时器
                    clearInterval(timeId)
                }
            }, 19)
        }
        Animate.prototype.step = function () {
            var t = +new Date() // 当前时间
            // 如果当前时间大于动画开始时间加上动画持续时间之和，说明动画已经结束，此时要修正小球的位置。
            if (t >= this.startTime + this.duration) {
                this.update(this.endPos) // 更新小球的 CSS 属性值
                return false
            }
            var pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration) // pos为小强当前位置
            this.update(pos)
        }
        Animate.prototype.update = function (pos) {
            this.dom.style[this.properName] = pos + 'px'
        }
        // 运动测试
        var dom = document.querySelector('#div1')
        var animate = new Animate(dom)
        animate.start('left', 500, 1000, 'easeIn')

        var dom2 = document.querySelector('#div2')
        var animate2 = new Animate(dom2)
        animate2.start('top', 500, 1000, 'sineaseOut')

        var dom3 = document.querySelector('#div3')
        var animate3 = new Animate(dom3)
        animate3.start('bottom', 800, 1000, 'sineaseOut')


    </script>

</body>

</html>